package io.lvdaxian.mybatis04.session.defaults;

import io.lvdaxian.mybatis04.mapping.BoundSql;
import io.lvdaxian.mybatis04.mapping.Environment;
import io.lvdaxian.mybatis04.mapping.MappedStatement;
import io.lvdaxian.mybatis04.session.Configuration;
import io.lvdaxian.mybatis04.session.SqlSession;

import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * DefaultSqlSession 是对象接口【SqlSession】的实现
 *
 * @author lihh
 */
public class DefaultSqlSession implements SqlSession {
  
  // 表示 控制上下文的配置文件
  private final Configuration configuration;
  
  public DefaultSqlSession(Configuration configuration) {
    this.configuration = configuration;
  }
  
  @Override
  public <T> T selectOne(String statement) {
    return (T) ("你被代理了！" + "方法：" + statement);
  }
  
  /**
   * 根据 statement 找到对应的sql，以及 通过参数【parameter】查询数据
   *
   * @param statement 能查询到对应sql的唯一id
   * @param parameter 实现的动态参数
   * @param <T>       动态泛型
   * @return 返回响应的值
   * @author lihh
   */
  @Override
  public <T> T selectOne(String statement, Object parameter) {
    // 根据 statement 查询 MappedStatement, 但是 MappedStatement 包括 参数类型 返回类型 以及 sql
    MappedStatement mappedStatement = configuration.getMappedStatement(statement);
    // 表示 当前数据源环境
    Environment environment = configuration.getEnvironment();
    
    try {
      // 通过 datasource 拿到 Connection
      Connection connection = environment.getDataSource().getConnection();
      // 绑定的sql
      BoundSql boundSql = mappedStatement.getBoundSql();
      
      PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSql());
      // 动态设置参数
      preparedStatement.setLong(1, Long.parseLong(((Object[]) parameter)[0].toString()));
      
      // 执行后 拿到结果集
      ResultSet resultSet = preparedStatement.executeQuery();
      List<T> objList = resultSet2Obj(resultSet, Class.forName(boundSql.getResultType()));
      return objList.get(0);
    } catch (SQLException | ClassNotFoundException e) {
      throw new RuntimeException(e);
    }
  }
  
  /**
   * 将 结果Set 转换 对象
   *
   * @param resultSet 结果集
   * @param clazz     表示转换的类
   * @param <T>       动态泛型
   * @return 返回集合对象
   * @author lihh
   */
  private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz) {
    List<T> list = new ArrayList<>();
    try {
      // 结果集的 元信息
      ResultSetMetaData metaData = resultSet.getMetaData();
      // 结果集的 列个数
      int columnCount = metaData.getColumnCount();
      
      // 每次遍历行值
      while (resultSet.next()) {
        // 通过方法【newInstance】拿到实例
        T obj = (T) clazz.newInstance();
        // 遍历 列的个数
        for (int i = 1; i <= columnCount; i++) {
          // 拿到的具体的值
          Object value = resultSet.getObject(i);
          // 拿到的 列名称
          String columnName = metaData.getColumnName(i);
          // 这里尝试拼接方法 eg: setName
          String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);
          Method method;
          if (value instanceof Timestamp) {
            method = clazz.getMethod(setMethod, Date.class);
          } else {
            method = clazz.getMethod(setMethod, value.getClass());
          }
          // 指定对象 set 方法
          method.invoke(obj, value);
        }
        list.add(obj);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return list;
  }
  
  
  @Override
  public <T> T getMapper(Class<T> clazz) {
    return configuration.getMapper(clazz, this);
  }
  
  @Override
  public Configuration getConfiguration() {
    return configuration;
  }
}
